#!/usr/bin/R --vanilla --slave -f
#
# TODO: Test these
#
# Common requests:
# 
#   pgi.object(a=1, foo="bar")
#
# Common responses:
#
#   return(pgi.object(
#       body="foo" some string
#       )
#
#   write foo.png
#   return(pgi.object(
#       `&body` = "foo.png"
#       )
#
#   calculate a data frame
#   return(pgi.object(
#       headers = pgi.object(
#          `Content-Type` = "application/csv"
#          )
#       `&body` = data.frame.to.csv(df)
#       )
#
#   calculate 2 data frame
#   return(pgi.object(
#       `&body` = data.frame.to.csv(df)
#       )

source('tnet.R')

# Log a test case
log.case <- function(s) {
  cat(paste('\t|', s, '\n'))
}

tnet.dump(123)
tnet.dump(123.4)

# Hm, exponential syntax may not be what we want
tnet.dump(1e4)
tnet.dump(1e12)
tnet.dump(1.234e12)

tnet.dump(T)
tnet.dump(F)

tnet.dump(NULL)

tnet.dump("hello world")

tnet.dump(c(1))
tnet.dump(c(4,5,6))

tnet.dump(
  list(foo="bar")
  )
tnet.dump(
  list(a="b", c="d")
  )

tnet.dump(
  list(foo=c(1,2,3))
  )

tnet.dump(
  list(foo=c(1,2,3), bar=c(4,5,6))
  )

#tnet.dump(
#  c( list(foo=c(1,2,3), bar=c(4,5,6)),
#     list(foo=c(7,8,9), bar=c(3,4,5)),
#  ))

tnet.dump(
  list(99)
  )

tnet.dump(
  list(1,2,3)
  )

tnet.dump(
  list(e="f")
  )

tnet.dump(
  list(x="y", w="z")
  )

tnet.dump(
  list(one="two", three="four", five="six")
  )

cat("Converting data frame to CSV\n")
print.data.frame(airquality)


# open a text connection for writing
data.frame.to.csv <- function(df) {
  # local=T makes a global variable!
  t <- textConnection("csv.lines", open='w', local=T)
  write.csv(airquality, file=t, row.names=F)
  close(t)
  # this becomes a vector of lines I think, so you have to separate with \n
  csv.str <- paste(csv.lines, collapse='\n')
  return(csv.str)
}

csv.str <- data.frame.to.csv(airquality)
cat(csv.str)
cat('\n')
cat(paste('CSV of length', nchar(csv.str), '\n'))

# Now test how fast it is to embed the CSV in tnet string body

print(tnet.load('0:~'))
print(tnet.load('1:1#'))
print(tnet.load.prefix('1:1#extra'))
print(tnet.load('4:5.55^'))
print(tnet.load('4:true!'))
print(tnet.load('5:false!'))
print(tnet.load('4:andy,'))

# list
cat('--\n')
print(tnet.load('4:1:6#]'))
cat('--\n')
print(tnet.load('8:1:6#1:7#]'))
cat('--\n')
# in R, c() is NULL I think
print(tnet.load('0:]'))

# issue: R doesn't have heterogeneous vectors, so this because c("6", "a")
# instead of c(6, "a")
print(tnet.load('8:1:6#1:a,]'))
cat('--\n')

# dict
print(tnet.load('0:}'))
cat('--\n')

print(tnet.load('8:1:a,1:3#}'))
cat('--\n')

log.case('named list of vectors')

cat(tnet.dump(list(a=c(1,2,3))))

#cat(tnet.dump(airquality))

# Weird -- is.na and is.integer are TRUE!
stopifnot(is.na(airquality[1][115,]))
stopifnot(is.integer(airquality[1][115,]))

cat('\n')
log.case('airquality')
cat(tnet.dump(airquality[1][115,]))
stopifnot(tnet.dump(airquality[1][115,]) == '0:~')

# Try to dump raw -- not supported for now.  We could support it
err <- try(tnet.dump(raw(length=3)))
stopifnot("try-error" == class(err))

s <- tnet.dump(c(13))
cat(s)
cat('\n')


test.special.types <- function() {
  # serialize
  int.list.s <- tnet.dump(c(7L,8L,9L))
  cat(int.list.s)
  cat('\n')

  num.list.s <- tnet.dump(c(7,8,9))
  cat(num.list.s)
  cat('\n')

  bool.list.s <- tnet.dump(c(T,F,T))
  cat(bool.list.s)
  cat('\n')

  string.list.s <- tnet.dump(c("x", "y", "z"))
  cat(string.list.s)
  cat('\n')

  na.list.s <- tnet.dump(c(NA, NA))
  cat(na.list.s)
  cat('\n')

  # deserialize
  int.list <- tnet.load(int.list.s)
  cat(as.character(int.list))
  cat('\n')
  stopifnot(c(7L,8L,9L) == int.list)

  num.list <- tnet.load(num.list.s)
  cat(as.character(num.list))
  cat('\n')
  stopifnot(c(7,8,9) == num.list)

  bool.list <- tnet.load(bool.list.s)
  cat(as.character(bool.list))
  cat('\n')
  stopifnot(c(T,F,T) == bool.list)

  string.list <- tnet.load(string.list.s)
  cat(as.character(string.list))
  cat('\n')
  stopifnot(c("x","y","z") == string.list)

  na.list <- tnet.load(na.list.s)
  cat(as.character(na.list))
  cat('\n')
  stopifnot(2 == length(na.list))
  stopifnot(is.na(na.list[[1]]) && is.na(na.list[[2]]))


  # serialize named vector
  n <- c(7L,8L,9L)
  names(n) <- c('x', 'y', 'z')
  named.vector.s <- tnet.dump(n)
  cat(named.vector.s)
  cat('\n')

  # deserialize named vector

  named.vector <- tnet.load(named.vector.s)
  cat(class(named.vector))
  cat('\n')
  cat(as.character(named.vector))
  cat('\n')
  stopifnot(c('x', 'y', 'z') == names(named.vector))
  stopifnot(named.vector$x == 7)
  stopifnot(named.vector$y == 8)
  stopifnot(named.vector$z == 9)

  err <- try(tnet.load('1:3,extra-bytes'))
  stopifnot("try-error" == class(err))
}

test.data.frame <- function() {
  # is.list(data.frame()) == T in R, so we don't need any special code for data
  # frames.
  d.str <- tnet.dump(data.frame(a=c(7,8,9), b=(c(3,4,5))))
  cat('\n')
  cat(d.str)
  cat('\n')
  obj <- tnet.load(d.str)

  # It gets deserialized into a list
  stopifnot(length(obj) == 2)  # list
  stopifnot(length(obj$a) == 3)
  stopifnot(length(obj$b) == 3)
  stopifnot(length(obj[[1]]) == 3)
  stopifnot(length(obj[[2]]) == 3)

  # But you can convert it to a data frame
  d <- as.data.frame(obj)

  stopifnot(length(d) == 2)
  stopifnot(length(d$a) == 3)
  stopifnot(length(d$b) == 3)

  # Data frames can be sliced by row/column; lists can't.
  stopifnot(d[1,] == c(7,3))
  stopifnot(d[,1] == c(7,8,9))
}

#
#tnet.dump(
#  ( pgi.object(foo=c(1,2,3), bar=c(4,5,6)),
#     pgi.object(foo=c(7,8,9), bar=c(3,4,5))
#  ))
#


#log.case('cannot readChar from textConnection')
#f = textConnection("3:abc,", encoding="bytes")
#length = scan(file=f, what="integer", sep=":", n=1)
#payload = readChar(f, n=1, useBytes=T)
#cat("yo")
#cat(length)
#cat("\n")
#cat(payload)
#cat("\n")


test.loadf.readf <- function() {
  # WTF, R is stupid -- textConnections don't allow readChar!  Have to
  # use a tmpfile.  WTF.
  test.str <- tnet.dump(list(a=3,b=4))
  tmp <- file('tmp.tnet', 'w')

  cat(test.str, file=tmp)  # copy 1
  cat(test.str, file=tmp)  # copy 2
  cat('EXTRA', file=tmp)  # extra data
  close(tmp)

  tmp <- file('tmp.tnet', 'r')
  value <- tnet.loadf(tmp)  # loadf on copy 1
  print(value)
  cat('--\n')
  stopifnot(value$a == 3)
  stopifnot(value$b == 4)
  stopifnot(length(value) == 2)

  str <- tnet.readf(tmp)  # readf on copy 2
  cat(str)
  stopifnot(str == '16:1:a$1:3^1:b$1:4^}')

  tmp <- file('tmp.tnet', 'rb', encoding='bytes')
  ch <- readChar(tmp, 1)
  cat(paste('CHAR', ch, class(ch), '\n'))

  tmp <- file('tmp.tnet', 'rb', encoding='bytes')
  length <- scan(tmp, integer(), sep=':', n=1)
  cat(paste('LENGTH', length, class(length), '\n'))
}

# Bug fix: slow path needed when using NA, so we don't get 2:NA^.  0:~ is the
# right value.
test.dump.with.na <- function() {
  s <- tnet.dump(c(1, NA))
  cat(s)
  stopifnot(s == '7:1:1^0:~]')
}


# CALL TEST FUNCTIONS

test.special.types()
test.data.frame()
test.loadf.readf()
test.dump.with.na()
